/**
 * 
 */
package speculoos.core;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import speculoos.utils.TypeHelper;
import speculoos.utils.TypeManipulator;
import speculoos.utils.VariableString;

/**
 * An mapper that modifies a map by providing it default values.
 * <p>
 * This mapper modifies the <code>input</code> object to add fields with
 * default values if they are not present. Instances of this validator are
 * constructed from an instance of Map that stores default - string - values for
 * keys.
 * </p>
 * <p>
 * The values of the original map from which this validator is constructed must
 * be String instances. These values are transformed using
 * {@link speculoos.utils.VariableString}  which are then evaluated at run time
 * using the input map as environment for substitution. This means that default
 * values can be expressed as syntactic substitutions of actual values.
 * </p>
 * 
 * @author abailly
 */
public class MapperDefaults implements Mapper,TypeManipulator {

	/*
	 * contains default values for keys
	 */
	private Map defaults;

	/*
	 * type helper to set values in object
	 */
	private TypeHelper typeHelper;

	/**
	 * Construct an empty defaults validator. This constructor is for
	 * programmatic adding of a map in configuration.
	 */
	public MapperDefaults() {
		this.defaults = new HashMap();
	}

	/**
	 * Create a validator with given map. The given map is used as is but it is
	 * guaranteed that its content will never be modified by this object.
	 * 
	 * @param map
	 *            a Map<String,String>
	 */
	public MapperDefaults(Map map) {
		this.defaults = makeVariables(map);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see speculoos.jndi.valid.Validator#validate(java.util.Map,
	 *      java.util.Map)
	 */
	public Object map(Object input, Map env) {
		for (Iterator i = defaults.entrySet().iterator(); i.hasNext();) {
			Map.Entry e = (Map.Entry) i.next();
			/* check default is present in input, or else add it */
			if (typeHelper.hasDefaultValue(input, (String) e.getKey()))
				typeHelper.setString(input, (String) e.getKey(),
						((VariableString) e.getValue()).instance(env));
		}
		return input;
	}

	/**
	 * @return Returns the defaults.
	 */
	public Map getDefaults() {
		return defaults;
	}

	/**
	 * The input map is <strong>not</strong> copied and it is never modified in
	 * this class.
	 * 
	 * @param defaults
	 *            The defaults to set.
	 */
	public void setDefaults(Map defaults) {
		this.defaults = makeVariables(defaults);
	}

	/*
	 * create VariableString instances from the given map content.
	 */
	private Map makeVariables(Map defaults2) {
		Map m = new HashMap();
		for (Iterator i = defaults2.entrySet().iterator(); i.hasNext();) {
			Map.Entry e = (Map.Entry) i.next();
			if (e.getValue() == null)
				continue;
			m.put(e.getKey(), new VariableString((String) e.getValue()));
		}
		return m;
	}

	/**
	 * @return Returns the typeHelper.
	 */
	public TypeHelper getTypeHelper() {
		return typeHelper;
	}

	/**
	 * @param typeHelper
	 *            The typeHelper to set.
	 */
	public void setTypeHelper(TypeHelper typeHelper) {
		this.typeHelper = typeHelper;
	}

	public String getName() {
		return "MapperDefaults";
	}

}
